package com.easyjpadao;

import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

/**
 *
 * @author Douglas
 */
public class GenericDao {
    private static GenericDao single;
    private EntityManagerFactory factory = null;
    private EntityManager manager = null;
    private Object obj = null;
    private List list = null;


    public GenericDao(String nameOfPersistenceUnit,HashMap parameters){
        try{
            factory = Persistence.createEntityManagerFactory(nameOfPersistenceUnit,parameters);
        }catch(Exception e){
            String error = e.getMessage();
            e.printStackTrace();
        }
    }

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }


    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }


    //inserts the entity object
    public String insert(Object obj) {
        String error = "";
        manager = factory.createEntityManager();
        EntityTransaction transaction = manager.getTransaction();
	try {
            transaction.begin();
            manager.persist(obj);
            transaction.commit();            
	} catch (Exception e) {
            transaction.rollback();
            error += "Error while inserting: " + e.getMessage();
	} finally {
            manager.close();
        }
        return error;
    }
    //find by integer id an entity object
    public Object findObjectById(Object obj, Integer id) {
        manager = factory.createEntityManager();
	EntityTransaction transaction = manager.getTransaction();

	Object found = null;
        try {
            transaction.begin();
            found = (Object) manager.find(obj.getClass() , id);
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            System.err.println("Error: " + e.getMessage());
	} finally {
            manager.close();
	}
            return found;
    }
    //find by Compound key in table the object
    public Object findObjectByCompoundkey(Object obj, Object PK) {
        manager = factory.createEntityManager();
	EntityTransaction transaction = manager.getTransaction();

	Object found = null;
	try {
            transaction.begin();
            found = (Object) manager.find(obj.getClass() , PK);
            transaction.commit();
	} catch (Exception e) {
            transaction.rollback();
            System.err.println("Erro: " + e.getMessage());
	} finally {
            manager.close();
	}
            return found;
    }
    //merge the entity object in database
    public String update(Object obj){
        String error = "";
        manager = factory.createEntityManager();
        EntityTransaction transaction = manager.getTransaction();
        try {
            transaction.begin();
            manager.merge(obj);
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            error = "Error while updating: "+e.getMessage();
        } finally {
            manager.close();
	}
        return error;
    }
    //delete by integer id an entity object
    public String deleteById(Object obj, int id){
        String error = "";
        manager = factory.createEntityManager();
        EntityTransaction transaction = manager.getTransaction();
        try {
            transaction.begin();
            manager.remove(manager.find(obj.getClass(),id));
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            error += "Error while deleting: "+e.getMessage();
	} finally {
            manager.close();
	}
        return error;
    }
    //delete by Compound key an entity object
    public String deleteByCompoundkey(Object obj, Object PK){
        String error = "";
        manager = factory.createEntityManager();
        EntityTransaction transaction = manager.getTransaction();
        try {
            transaction.begin();
            manager.remove(manager.find(obj.getClass(),PK));
            transaction.commit();
	} catch (Exception e) {
            transaction.rollback();
            error += "Error while deleting by composed key: "+e.getMessage();
        } finally {
            manager.close();
        }
        return error;
    }
    //list all itens in a table by entity object, condition and orders by can be declared
    public List listAll(Object obj, String condition, String ordenation){
        if (obj == null){
            return null;
        }else{
            String sql = "SELECT * FROM "+obj.getClass().getSimpleName();
            List ownlist = null;
            if(! condition.trim().isEmpty()){
                sql+=" WHERE "+condition;
            }
            if(! ordenation.trim().isEmpty()){
                sql+=" ORDER BY "+ordenation;
            }
            manager = factory.createEntityManager();
            EntityTransaction transaction = manager.getTransaction();
            try {
                transaction.begin();
                Query qry = manager.createNativeQuery(sql);
                ownlist = (Vector) qry.getResultList();
    			transaction.commit();
            } catch (Exception e) {
    		transaction.rollback();
            } finally {
    		manager.close();
                this.setList(ownlist);
            }
           return ownlist;
        }
    }
    //execute an especif query on database
    public List executeQuery(String sql){
        List ownlist = null;
        manager = factory.createEntityManager();
        EntityTransaction transaction = manager.getTransaction();
    	try {
            transaction.begin();
            Query qry = manager.createNativeQuery(sql);
            ownlist = (Vector) qry.getResultList();
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
    	} finally {
            manager.close();
    	}
        return ownlist;
    }
    //execute an especific update on database
    public String executeUpdate(String sql){
        String error = null;
        manager = factory.createEntityManager();
	EntityTransaction transaction = manager.getTransaction();
	try {
            transaction.begin();
            Query qry = manager.createNativeQuery(sql);
            qry.executeUpdate();
            transaction.commit();
	} catch (Exception e) {
            transaction.rollback();
            error = "Error while was executing update: "+e.getMessage();
	} finally {
            manager.close();
	}
        return error;
    }
    //returns that the table has dependences in table declared. Condition you gotta declare the FK field
    public boolean hasDependencies(Object obj, String condition){
        int dependencies = 0;
        boolean result = false;
        String sql = "SELECT count(*) FROM "+obj.getClass().getSimpleName();
        if(! condition.isEmpty()){
            sql+=" WHERE "+condition;
        }
        manager = factory.createEntityManager();
	EntityTransaction transaction = manager.getTransaction();
	try {
            transaction.begin();
            Query qry = manager.createNativeQuery(sql);
            dependencies = Integer.valueOf(qry.getSingleResult().toString());
            transaction.commit();
	} catch (Exception e) {
            transaction.rollback();
            result = false;
        } finally {
            if(dependencies > 0){
                result = true;
            }
            manager.close();
        }
        return result;
    }
    //returns an array of the first object in the list returned in listAll method
     public Object first(){
         if(! this.list.isEmpty()){
            this.setObj(getList().get(0));
         }else{
             this.setObj(null);
         }
         return this.getObj();
     }
     //returns an array of the previous object in the list returned in listAll method
     public Object previous(){
         if((! this.list.isEmpty())&&(this.list.indexOf(obj) != 0)){
            this.setObj(this.list.get(list.indexOf(obj) - 1));
         }            
         return this.getObj();
     }
     //returns an array of the next object in the list returned in listAll method
     public Object next(){
         if((! this.list.isEmpty()) && (this.list.indexOf(obj) != this.list.size() - 1)){
            this.setObj(this.list.get(list.indexOf(obj) + 1));
         }
         return this.getObj();
     }
     //returns an array of the last object in the list returned in listAll method
     public Object last(){
         if(! this.list.isEmpty()){
            this.setObj(this.list.get(list.size()-1));
         }else{
             this.setObj(null);
         }
         return this.getObj();
     }
    //can return an genericDao just created or a null class
    public static GenericDao getInstance(){
        if (single == null) {
            single = new GenericDao("",null);
	}
            return single;
    }

}
